﻿namespace PlanckSys.Jira.RssJiraRedirector
{
    #region Imports
    using Console = System.Console;
    using IAsyncResult = System.IAsyncResult;
    using HttpStatusCode = System.Net.HttpStatusCode;
    using NetworkCredential = System.Net.NetworkCredential;
    using WebProxy = System.Net.WebProxy;
    using WebRequest = System.Net.WebRequest;
    using WebResponse = System.Net.WebResponse;
    using Assembly = System.Reflection.Assembly;
    using BindingFlags = System.Reflection.BindingFlags;
    using FieldInfo = System.Reflection.FieldInfo;
    using String = System.String;
    using Type = System.Type;
    using Uri = System.Uri;
    using Int32 = System.Int32;
    #endregion


    public class JiraDao
    {
        private static string jiraLogin1 = "You are not logged in";
        private static string jiraLogin2 = "Login Required";
        private static string jiraLogin3 = "You must log in to access this page.";
        private static string jiraUserField = "os_username";
        private static string jiraPassField = "os_password";
        private static string jiraErr1 = "formErrors";
        private static string jiraErr2 = "formErrorArea";
        private static string jiraErr3 = "errorArea";

        private string _JiraBaseUrl = "";
        public string JiraBaseUrl
        {
            get
            {
                return this._JiraBaseUrl;
            }
        }

        public JiraDao(string baseJiraURL)
        {
            this._JiraBaseUrl = baseJiraURL;
        }

        public JiraDao()
        {

        }

        public void AuthProxy(NoRedirectCookieAwareWebClient httpclient, string jiraProxyHost, string jiraProxyUser, string jiraProxyPass)
        {
            if (!(string.IsNullOrWhiteSpace(jiraProxyHost)))
            {
                WebProxy myProxy = new WebProxy();
                Uri newUri = new Uri(jiraProxyHost);

                // Associate the newUri object to 'myProxy' object so that new myProxy settings can be set.
                myProxy.Address = newUri;
                if (!(jiraProxyUser == null || string.IsNullOrWhiteSpace(jiraProxyUser)))
                {
                    string pass = (string.IsNullOrWhiteSpace(jiraProxyPass)) ? null : jiraProxyPass;

                    int backSlash = jiraProxyUser.IndexOf('\\');
                    if (backSlash > 0)
                    {
                        String domain = jiraProxyUser.Substring(0, backSlash);
                        String user = jiraProxyUser.Substring(backSlash + 1);
                        String computer = "CBAD4-KCILR1";

                        myProxy.Credentials = new NetworkCredential(user, pass, domain);

                    }
                    else
                    {
                        myProxy.Credentials = new NetworkCredential(jiraProxyUser, pass);
                    }

                }
                httpclient.Proxy = myProxy;
            }
        }

        /// <summary>
        /// logs into jira web form for us
        /// </summary>
        /// <param name="httpclient"></param>
        ///  throws System.Security.Authentication.InvalidCredentialException, System.Data.DataException
        public void DoLogin(NoRedirectCookieAwareWebClient httpclient, string jiraUser, string jiraPass)
        {
            try
            {

                System.Collections.Specialized.NameValueCollection postData = new System.Collections.Specialized.NameValueCollection();
                postData.Add(jiraUserField, jiraUser);
                postData.Add(jiraPassField, jiraPass);
                postData.Add("os_destination", "/secure");
                string loginUrl = _JiraBaseUrl + "/login.jsp";
                byte[] results = httpclient.UploadValues(loginUrl, postData);
                string html = System.Text.Encoding.ASCII.GetString(results);

                if (html.IndexOf(jiraUserField) >= 0)
                {
                    Console.WriteLine("code:" + httpclient.StatusCode);
                    Console.WriteLine(html);
                    throw new System.Security.Authentication.InvalidCredentialException("Failed to login to JIRA as " + jiraUser);
                }
            }
            catch (System.Security.Authentication.InvalidCredentialException ice)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Auth Failed", ice);
            }
            catch (System.Exception e)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("IO failure", e);
            }
        }

        public string ProxeyUrl(NoRedirectCookieAwareWebClient httpclient, string url)
        {

            if (string.IsNullOrWhiteSpace(url))
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira url '" + url + "' does not exists");
            }
            string text = null;


            try
            {
                // string issueRssURL = _JiraBaseUrl + "/si/jira.issueviews:issue-xml/" + jiraTicket + "/" + jiraTicket + ".xml";
                try
                {
                    text = httpclient.DownloadString(url);
                }
                catch (System.Net.WebException we)
                {
                    if (httpclient.StatusCode != HttpStatusCode.NotFound)
                    {
                        throw;
                    }
                }

                if (httpclient.StatusCode != HttpStatusCode.NotFound)
                {
                    if ((text.IndexOf("<rss") < 0))
                    {
                        if ((text.IndexOf(jiraUserField) >= 0) &&
                            (((text.IndexOf(jiraLogin2) > 0) || (text.IndexOf(jiraLogin1) > 0) || (text.IndexOf(jiraLogin3) > 0)))
                            )
                        {
                            throw new System.Security.Authentication.AuthenticationException("Jira Login Required or project access for '" + url + "'");
                        }
                    }
                }
                else
                {
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira url " + url + " does not exists");
                }

            }
            catch (System.Security.Authentication.AuthenticationException ae)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Auth Failed", ae);
            }
            catch (PlanckSys.Jira.RssJiraRedirector.JiraDaoException)
            {
                throw;
            }
            catch (System.Exception e)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("IO failure", e);
            }

            return text;
        }

        /// <summary>
        /// get the RSS feeds for particular Jira ticket
        /// </summary>
        /// <param name="httpclient"></param>
        /// <param name="jiraTicket"></param>
        /// <returns></returns>
        /// throws System.Data.DataException, System.Security.Authentication.AuthenticationException
        public string GetIssueRSS(NoRedirectCookieAwareWebClient httpclient, String jiraTicket)
        {
            if (string.IsNullOrWhiteSpace(jiraTicket))
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket blank '" + jiraTicket + "' does not exists");
            }
            string text = null;


            try
            {
                string issueRssURL = _JiraBaseUrl + "/si/jira.issueviews:issue-xml/" + jiraTicket + "/" + jiraTicket + ".xml";
                try
                {
                    text = httpclient.DownloadString(issueRssURL);
                }
                catch (System.Net.WebException we)
                {
                    if (httpclient.StatusCode != HttpStatusCode.NotFound)
                    {
                        throw;
                    }
                }

                if (httpclient.StatusCode != HttpStatusCode.NotFound)
                {
                    if ((text.IndexOf("<rss") < 0) || (text.IndexOf("</key>") < 0))
                    {
                        if ((text.IndexOf(jiraUserField) >= 0) &&
                            (((text.IndexOf(jiraLogin2) > 0) || (text.IndexOf(jiraLogin1) > 0) || (text.IndexOf(jiraLogin3) > 0)))
                            )
                        {
                            throw new System.Security.Authentication.AuthenticationException("Jira Login Required or project access for '" + jiraTicket + "'");
                        }
                        else
                        {
                            throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket " + jiraTicket + " does not exists");
                        }
                    }
                }
                else
                {
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket " + jiraTicket + " does not exists");
                }

            }
            catch (System.Security.Authentication.AuthenticationException ae)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Auth Failed", ae);
            }
            catch (PlanckSys.Jira.RssJiraRedirector.JiraDaoException)
            {
                throw;
            }
            catch (System.Exception e)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("IO failure", e);
            }

            return text;
        }

        /// <summary>
        /// get status from RSS feed for particular ticket
        /// </summary>
        /// <param name="rssfeedResults"></param>
        /// <param name="jiraTicket"></param>
        /// <returns></returns>
        /// throws System.Data.DataException
        public int GetJiraStatus(string rssfeedResults, string jiraTicket)
        {

            int id = -1;
            //<status id="5" iconUrl="http://pmodbp:8080/images/icons/status_resolved.gif">Resolved</status>
            if (rssfeedResults == null || rssfeedResults.Length <= 0)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException(
                    "Jira Ticket Status for " + jiraTicket + " does not exists");
            }
            else
            {
                string prefix = "<status id=\"";
                string suffix1 = "</status>";
                string suffix2 = "\" iconUrl=";
                int idxStart = rssfeedResults.IndexOf(prefix);
                int idxEnd = rssfeedResults.IndexOf(suffix1);
                if (idxStart < 0 || idxEnd < 0)
                {
                    Console.WriteLine("Jira Ticket Status NOT EXISTS : " + rssfeedResults);
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Status for " + jiraTicket + " does not exists");
                }
                else
                {
                    string idStr = rssfeedResults.Substring(idxStart + prefix.Length, idxEnd - (idxStart + prefix.Length));
                    int idxEnd3 = idStr.IndexOf(suffix2);
                    if (idxEnd3 < 0)
                    {
                        Console.WriteLine("Jira Ticket Status NOT EXISTS : " + rssfeedResults);
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Status for " + jiraTicket + " does not exists");
                    }
                    else
                    {
                        idStr = idStr.Substring(0, idxEnd3);
                        try
                        {
                            id = Int32.Parse(idStr);
                        }
                        catch (System.FormatException e)
                        {
                            throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Status for " + jiraTicket + " does not exists", e);
                        }
                    }
                }
            }
            return id;
        }


        /// <summary>
        /// get Priority from RSS feed for particular ticket
        /// </summary>
        /// <param name="rssfeedResults"></param>
        /// <param name="jiraTicket"></param>
        /// <returns></returns>
        /// throws System.Data.DataException
        public int GetJiraPriority(String rssfeedResults, String jiraTicket)
        {
            int id = -1;
            //<priority id="1" iconUrl="http://pmodbp:8080/images/icons/priority_blocker.gif">Emergency Fix</priority>
            if (rssfeedResults == null || rssfeedResults.Length <= 0)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Priority for " + jiraTicket + " does not exists");
            }
            else
            {
                string prefix = "<priority id=\"";
                string suffix1 = "</priority>";
                string suffix2 = "\" iconUrl=";
                int idxStart = rssfeedResults.IndexOf(prefix);
                int idxEnd = rssfeedResults.IndexOf(suffix1);
                if (idxStart < 0 || idxEnd < 0)
                {
                    Console.WriteLine("Jira Ticket Priority NOT EXISTS : " + rssfeedResults);
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Priority for " + jiraTicket + " does not exists");
                }
                else
                {

                    string idStr = rssfeedResults.Substring(idxStart + prefix.Length, idxEnd - (idxStart + prefix.Length));
                    int idxEnd3 = idStr.IndexOf(suffix2);
                    if (idxEnd3 < 0)
                    {
                        Console.WriteLine("Jira Ticket Priority NOT EXISTS : " + rssfeedResults);
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException(
                            "Jira Ticket Priority for " + jiraTicket + " does not exists");
                    }
                    else
                    {
                        idStr = idStr.Substring(0, idxEnd3);
                        try
                        {
                            id = Int32.Parse(idStr);
                        }
                        catch (System.FormatException e)
                        {
                            throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Priority for " + jiraTicket + " does not exists", e);
                        }
                    }
                }
            }
            return id;
        }

        /// <summary>
        /// get IssueID from RSS feed for particular ticket
        /// </summary>
        /// <param name="rssfeedResults"></param>
        /// <param name="jiraTicket"></param>
        /// <returns></returns>
        /// throws System.Data.DataException
        public int GetJiraIssueID(String rssfeedResults, String jiraTicket)
        {
            int id = -1;
            //<key id="234473">OSA-79308</key>
            if (rssfeedResults == null || rssfeedResults.Length <= 0)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Issue ID for " + jiraTicket + " does not exists");
            }
            else
            {
                string prefix = "<key id=\"";
                string suffix = "\">" + jiraTicket + "</key>";
                int idxStart = rssfeedResults.IndexOf(prefix);
                int idxEnd = rssfeedResults.IndexOf(suffix);
                if (idxStart < 0 || idxEnd < 0)
                {
                    Console.WriteLine("Jira Ticket Issue ID NOT EXISTS : " + rssfeedResults);
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Issue ID for " + jiraTicket + " does not exists");
                }
                else
                {


                    string idStr = rssfeedResults.Substring(idxStart + prefix.Length, idxEnd - (idxStart + prefix.Length));
                    try
                    {
                        id = Int32.Parse(idStr);
                    }
                    catch (System.FormatException e)
                    {
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Issue ID for " + jiraTicket + " does not exists", e);
                    }
                }
            }
            return id;
        }

        /// <summary>
        /// get JiraSecurityToken from issue webpage for particular ticket
        /// </summary>
        /// <param name="request"></param>
        /// <param name="jiraIssueID"></param>
        /// <param name="jiraTicket"></param>
        /// <returns></returns>
        /// System.Data.DataException System.Security.Authentication.AuthenticationException
        public string GetJiraSecurityToken(NoRedirectCookieAwareWebClient request, int jiraIssueID, String jiraTicket)
        {
            try
            {
                string commentUrl = _JiraBaseUrl + "/secure/AddComment!default.jspa?id=" + jiraIssueID;
                string result = request.DownloadString(commentUrl);

                if (request.StatusCode != HttpStatusCode.NotFound)
                {
                    if ((result.IndexOf("Add Comment") < 0))
                    {
                        if ((result.IndexOf(jiraUserField) >= 0) &&
                            (((result.IndexOf(jiraLogin2) > 0) || (result.IndexOf(jiraLogin1) > 0) || (result.IndexOf(jiraLogin3) > 0)))
                            )
                        {
                            throw new System.Security.Authentication.AuthenticationException("Jira Login Required or project access for '" + jiraTicket + "'");
                        }
                        else
                        {
                            Console.WriteLine("code:" + request.StatusCode);
                            Console.WriteLine(result);
                            throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket " + jiraTicket + " does not exists");
                        }
                    }

                    string prefix = "<meta id=\"atlassian-token\" name=\"atlassian-token\" content=\"";
                    string suffix = "\">";
                    int idxStart = result.IndexOf(prefix);
                    int idxEnd = result.IndexOf(suffix, idxStart);
                    if (idxStart < 0 || idxEnd < 0)
                    {
                        Console.WriteLine("Jira Ticket Security Token does NOT EXISTS : " + result);
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket Security Token for " + jiraTicket + " does not exists");
                    }
                    else
                    {
                        string idStr = result.Substring(idxStart + prefix.Length, idxEnd - (idxStart + prefix.Length));
                        return idStr;
                    }
                }
                else
                {
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Ticket " + jiraTicket + " does not exists");
                }
            }
            catch (System.Security.Authentication.AuthenticationException ae)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Auth Failed", ae);
            }
            catch (PlanckSys.Jira.RssJiraRedirector.JiraDaoException)
            {
                throw;
            }
            catch (System.Exception e)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("IO failure", e);
            }
        }

        /// <summary>
        /// get JiraComment on particular ticket
        /// </summary>
        /// <param name="request"></param>
        /// <param name="rss"></param>
        /// <param name="jiraTicket"></param>
        /// <param name="comment"></param>
        /// System.Data.DataException System.Security.Authentication.AuthenticationException
        public void DoComment(NoRedirectCookieAwareWebClient httpclient, String rss, String jiraTicket, String comment, bool oldJiraVersion)
        {
            try
            {
                if (comment == null || comment.Length <= 0)
                {
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Comment failed for Jira Ticket " + jiraTicket + " because comment was blank");
                }
                int jiraIssueID = GetJiraIssueID(rss, jiraTicket);

                string jiraSecurityToken = null;
                if (!oldJiraVersion)
                {
                    jiraSecurityToken = GetJiraSecurityToken(httpclient, jiraIssueID, jiraTicket);
                }

                string commentUrl = _JiraBaseUrl + "/secure/AddComment.jspa";

                System.Collections.Specialized.NameValueCollection postData = new System.Collections.Specialized.NameValueCollection();
                postData.Add("id", "" + jiraIssueID);
                postData.Add("commentLevel", "");//blank is all users
                postData.Add("comment", comment);
                postData.Add("atl_token", jiraSecurityToken);

                byte[] results = httpclient.UploadValues(commentUrl, postData);
                string html = System.Text.Encoding.ASCII.GetString(results);

                if (httpclient.StatusCode == HttpStatusCode.Redirect)
                {

                    string h = httpclient.ResponseHeaders["Location"];
                    if (string.IsNullOrWhiteSpace(h))
                    {
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("(1) comment failed because Jira Ticket " + jiraTicket + " does not exists");
                    }


                    string urlRedirectResp = _JiraBaseUrl + "/browse/" + jiraTicket;
                    urlRedirectResp = urlRedirectResp.ToLower();
                    int idxStart = h.ToLower().IndexOf(urlRedirectResp);

                    if (idxStart > -1)
                    {
                        //success
                        return;
                    }
                    else
                    {
                        Console.WriteLine("code:" + httpclient.StatusCode);
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("(1) comment failed because Jira Ticket " + jiraTicket + " does not exists");
                    }
                }
                else
                {


                    if ((html.IndexOf(jiraUserField) >= 0) &&
                         (((html.IndexOf(jiraLogin2) > 0) || (html.IndexOf(jiraLogin1) > 0) || (html.IndexOf(jiraLogin3) > 0)))
                        )
                    {
                        throw new System.Security.Authentication.AuthenticationException("Jira Login Required");
                    }
                    else if ((html.IndexOf(jiraErr1) > 0) || (html.IndexOf(jiraErr2) > 0) || (html.IndexOf(jiraErr3) > 0))
                    {
                        Console.WriteLine("code:" + httpclient.StatusCode);
                        Console.WriteLine(html);
                        throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("(2) comment failed because Jira Ticket " + jiraTicket + " does not exists");
                    }
                    Console.WriteLine("code:" + httpclient.StatusCode);
                    Console.WriteLine(html);
                    throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("(3) comment failed because Jira Ticket " + jiraTicket + " does not exists");
                }
            }
            catch (System.Security.Authentication.AuthenticationException ae)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("Jira Auth Failed", ae);
            }
            catch (PlanckSys.Jira.RssJiraRedirector.JiraDaoException)
            {
                throw;
            }
            catch (System.Exception e)
            {
                throw new PlanckSys.Jira.RssJiraRedirector.JiraDaoException("IO failure", e);
            }
        }
    }

    [System.ComponentModel.DesignerCategory("Code")]
    public class NoRedirectCookieAwareWebClient : System.Net.WebClient
    {
        private System.Net.CookieContainer Cookies = new System.Net.CookieContainer();

        private HttpStatusCode _currentStatusCode = HttpStatusCode.SeeOther;

        public HttpStatusCode StatusCode
        {
            get
            {
                return _currentStatusCode;
            }
        }

        protected override System.Net.WebRequest GetWebRequest(Uri address)
        {
            SetAllowUnsafeHeaderParsing20();
            System.Net.WebRequest request = base.GetWebRequest(address);


            if (request is System.Net.HttpWebRequest)
            {
                System.Net.HttpWebRequest hwr = request as System.Net.HttpWebRequest;
                hwr.CookieContainer = Cookies;
                // ** disable auto-redirection **
                hwr.AllowAutoRedirect = false;

            }
            return request;
        }

        /// <summary>
        /// allows headers with space in it
        /// </summary>
        /// <returns></returns>
        public static bool SetAllowUnsafeHeaderParsing20()
        {
            //Get the assembly that contains the internal class
            Assembly aNetAssembly = Assembly.GetAssembly(typeof(System.Net.Configuration.SettingsSection));
            if (aNetAssembly != null)
            {
                //Use the assembly in order to get the internal type for the internal class
                Type aSettingsType = aNetAssembly.GetType("System.Net.Configuration.SettingsSectionInternal");
                if (aSettingsType != null)
                {
                    //Use the internal static property to get an instance of the internal settings class.
                    //If the static instance isn't created allready the property will create it for us.
                    object anInstance = aSettingsType.InvokeMember("Section",
                      BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.NonPublic, null, null, new object[] { });

                    if (anInstance != null)
                    {
                        //Locate the private bool field that tells the framework is unsafe header parsing should be allowed or not
                        FieldInfo aUseUnsafeHeaderParsing = aSettingsType.GetField("useUnsafeHeaderParsing", BindingFlags.NonPublic | BindingFlags.Instance);
                        if (aUseUnsafeHeaderParsing != null)
                        {
                            aUseUnsafeHeaderParsing.SetValue(anInstance, true);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
        {
            try
            {
                System.Net.WebResponse resp = base.GetWebResponse(request, result);
                if (resp is System.Net.HttpWebResponse)
                {
                    System.Net.HttpWebResponse hwr = resp as System.Net.HttpWebResponse;
                    _currentStatusCode = hwr.StatusCode;
                }
                return resp;
            }
            catch (System.Net.WebException we)
            {
                if (we.Message.EndsWith("(404) Not Found."))
                {
                    _currentStatusCode = HttpStatusCode.NotFound;
                }

                throw;
            }
        }
        protected override WebResponse GetWebResponse(WebRequest request)
        {
            try
            {
                System.Net.WebResponse resp = base.GetWebResponse(request);
                if (resp is System.Net.HttpWebResponse)
                {
                    System.Net.HttpWebResponse hwr = resp as System.Net.HttpWebResponse;
                    _currentStatusCode = hwr.StatusCode;
                }
                return resp;
            }
            catch (System.Net.WebException we)
            {
                if (we.Message.EndsWith("(404) Not Found."))
                {
                    _currentStatusCode = HttpStatusCode.NotFound;
                }
                else if (we.Message.EndsWith("(500) Internal Server Error."))
                {
                    _currentStatusCode = HttpStatusCode.InternalServerError;
                }

                throw;
            }
        }
    }
}
